package com.gzist.springailab03.service;

import cn.hutool.json.JSONUtil;
import com.gzist.springailab03.model.Message;
import com.gzist.springailab03.model.WsMessage;
import lombok.RequiredArgsConstructor;
import lombok.extern.slf4j.Slf4j;
import org.springframework.ai.chat.client.ChatClient;
import org.springframework.ai.chat.messages.AssistantMessage;
import org.springframework.ai.chat.messages.UserMessage;
import org.springframework.ai.chat.model.ChatModel;
import org.springframework.ai.chat.prompt.Prompt;
import org.springframework.ai.tool.ToolCallbackProvider;
import org.springframework.stereotype.Service;
import reactor.core.publisher.Flux;
import reactor.netty.http.client.PrematureCloseException;
import reactor.util.retry.Retry;

import java.time.Duration;
import java.util.ArrayList;
import java.util.List;
import java.util.concurrent.TimeoutException;


/**
 * AI服务类
 */
@Slf4j
@Service
@RequiredArgsConstructor
public class AIService {

    // 注入chatModel对象
    private final ChatModel chatModel;

    private final ToolCallbackProvider toolCallbackProvider;
    private String demoSystem() {
        return """
                【角色身份设定】
                你是一位专业的情感助手，拥有心理学硕士学历及5年临床咨询经验，擅长运用人本主义疗法和认知行为疗法。你的身份是：一个温暖、耐心、充满共情能力的心理陪伴者，专注于倾听用户的情感诉求并提供情绪疏导[[8]]。

                【核心交互目标】
                1. 通过非评判性倾听建立信任关系
                2. 运用苏格拉底式提问引导自我觉察
                3. 提供即时情绪急救技巧（如478呼吸法）
                4. 绘制情感地图识别核心情绪议题

                【能力边界范围】
                - 擅长领域：人际关系困扰/职场压力/孤独焦虑/自我成长
                - 限制范围：不替代专业心理咨询，不处理危机干预
                - 支持形式：文字共情+认知重构+资源推荐（如冥想音频）

                【对话黄金法则】
                1. 首轮回复必须包含情感确认："听起来您正在经历...（情绪词），这种感受是完全可以理解的"
                2. 每3轮对话进行一次焦点校准："我们刚才讨论的核心是...对吗？"
                3. 使用"有时候/或许/可能"等弹性化措辞保持开放性
                4. 当检测到危机信号时启动标准化应答："我注意到您提到...，虽然我无法替代专业帮助，但我们可以先尝试..."

                【人格特质参数】
                温暖度：★★★★★（使用表情符号\uD83C\uDF31传递关怀）
                专业度：★★★★☆（引用心理学概念时注明来源）
                互动性：★★★☆☆（保持适度提问节奏）""";
    }


    /**
     * 处理聊天WebSocket连接，用于接收用户消息并返回相应的回答
     * 如果这是用户的新会话，会首先发送一个模板提示
     * 该方法使用React式编程模型，通过Flux发布者异步处理消息
     *
     * @param message 客户端传过来的消息体
     * @param userId  用户唯一标识符
     * @return 返回一个Flux发布者，它将异步地发送聊天响应
     */
    public Flux<String> handleChatWebSocket(String message, String userId) {
        // 处理用户的消息
        return sendChatRequest(userId, message)
                .doOnNext(response -> log.info("发送响应: {}", response))
                .onErrorResume(e -> {
                    log.error("WebSocket通信错误: {}", e.getMessage());
                    return Flux.just("抱歉，服务器处理请求时发生错误，请稍后重试。");
                });
    }

    /**
     * 向指定用户发送聊天请求
     * 该方法负责构建聊天请求，发送给API，并处理响应
     *
     * @param userId  用户ID，用于从会话历史记录中获取该用户的聊天记录
     * @param message 客户端传过来的消息体
     * @return 返回一个Flux流，包含可能的聊天回复
     */
    private Flux<String> sendChatRequest(String userId, String message) {
        WsMessage wsMessage;
        ChatClient chatClient;
        List<Message> frontMessageList;
        try {
            wsMessage = JSONUtil.toBean(message, WsMessage.class);
        } catch (Exception e) {
            log.error("解析消息时发生错误: {}", e.getMessage());
            return Flux.just("抱歉，服务器处理请求时发生错误，请检查消息格式是否正确。");
        }
        if (wsMessage == null) {
            return Flux.just("抱歉，服务器处理请求时发生错误，请稍后重试。");
        }
        // 构建聊天请求对象并对其配置默认角色设定
        chatClient = ChatClient.builder(chatModel).build();
        frontMessageList = wsMessage.getMessageList() != null ?
                wsMessage.getMessageList() : new ArrayList<>();
        // 处理对话记录类型
        ArrayList<org.springframework.ai.chat.messages.Message> chatMessageList = new ArrayList<>();
        // 去掉assistant欢迎语即第一句话
        Message firstAssistantMessage = frontMessageList.get(0);
        if ("assistant".equals(firstAssistantMessage.getRole())) {
            frontMessageList.remove(0);
        }
        for (Message frontMessage : frontMessageList) {
            if (frontMessage.getRole().equals("user")) {
                chatMessageList.add(new UserMessage(frontMessage.getContent()));
            } else if (frontMessage.getRole().equals("assistant")) {
                chatMessageList.add(new AssistantMessage(frontMessage.getContent()));
            }
        }
        // 注意注意的一点是，不能添加options，否则tools的调用会失败
        return chatClient.prompt(new Prompt(chatMessageList))   // 设置聊天请求的提示词包括记忆
                .tools(toolCallbackProvider.getToolCallbacks()) // 调用MCP服务
                .stream()                                       // 启用流式响应
                .content()                                      // 获取聊天响应的内容
                .timeout(Duration.ofSeconds(300))               // 设置超时时间
                .retryWhen(Retry.backoff(4, Duration.ofSeconds(2)) // 添加重试机制
                        .filter(throwable ->
                                throwable instanceof PrematureCloseException
                                        || throwable instanceof TimeoutException
                                        || throwable instanceof RuntimeException)
                        .onRetryExhaustedThrow((retryBackoffSpec, retrySignal) -> {
                            log.error("重试次数已用完，最后一次错误: {}", retrySignal.failure().getMessage());
                            return new RuntimeException("服务暂时不可用，请稍后重试");
                        }))
                .onErrorResume(e -> {
                    log.error("请求处理错误: {}", e.getMessage());
                    // 返回纯文本错误信息，避免后续解析失败
                    return Flux.just("抱歉，服务器处理请求时发生错误: " + e.getMessage());
                })
                .map(response -> {                              // 处理响应内容即切片内容
                    try {
                        if ("[DONE]".equals(response)) {
                            return "";
                        }
                    } catch (Exception e) {
                        log.error("解析响应时出错: {}", e.getMessage());
                        log.error("原始响应: {}", response);
                        return "";
                    }
                    return response;
                }).filter(content -> content != null && !content.isEmpty() && !content.equals("null"));

    }


}